import java.util.Vector;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

import java.sql.Time;
import org.xml.sax.XMLReader;
import org.xml.sax.SAXException;
import com.sun.org.apache.xerces.internal.parsers.SAXParser;


/******************************************************************************************
This class contains the particular normal trajectories defined
for a specific Scene. An instance of TrajectoryAnalizer checks if
every object is following some of the normal trajectorys and 
calculates the normality degrees.

Attributes:

1)file: name of the file that contains the scene definition.
2) v_normal_trajectories: normal trajectories for a particular scene (see Trajectory class).
3) hm_objects: information about every object being analysed. The index keys used to access 
to the objects stored in the HashMap correspond with the object's names, for example: obj0, 
obj1, ... see MonitoredObject class.
4) now: TrajectoryAnalyzer continuosly recieves information about monitored objects. The
attribute "now" represents the current time instant use to check temporal constraints.
 ******************************************************************************************/


class TrajectoryAnalyzer{

    protected String file; 
    protected HashMap v_normal_trajectories; 
    protected HashMap hm_objects;
    protected Time now;
       

    public TrajectoryAnalyzer(String file){
    	this.file = file;	
    	v_normal_trajectories = new HashMap();
    	hm_objects = new HashMap();
    }//end Constructor

    public HashMap getNormalTrajectories(){return this.v_normal_trajectories;}
    
    /*
      loadKnowledge: this method parsers the XML that contains the Scene information. 
    */
    public void loadKnowledge(){
	try{
	    
	    MyXMLHandlerSceneTA handler = new MyXMLHandlerSceneTA(this.v_normal_trajectories);
	    XMLReader parser = new SAXParser();
	    parser.setContentHandler(handler); 
	    parser.parse(this.file);
	    
	}catch(Exception e){
	    System.out.println("Error while processing xml file: "+e);		
	}
    }//end loadKnowledge

    
    
    public ObjectLog analysis(ObjectEvent oe){
	//this method receives new information about an object in a particular frame. 
	//Each time the system recieves a new event, it performs the trajectory analysis 
	//process in order to update normality values and satisfaction degrees.
	
	//Spatial analysis
	spatialAnalysis(oe);
	//Rol Analysis
	roleAnalysis(oe);
	//Temporal analysis
	temporalAnalysis(oe);
	//updating normality degree according to the constraint satisfaction values.
	ObjectLog obj = (ObjectLog) this.hm_objects.get(oe.getId());
	obj.updateNormalityDegree();
	
	return obj;
    }//end method.
    
    private void roleAnalysis(ObjectEvent oe){
	ObjectLog obj = (ObjectLog) this.hm_objects.get(oe.getId());

	HashMap v_trajectories = obj.getAssociatedTrajectories();
	Iterator it = v_trajectories.entrySet().iterator();
	Vector<MembershipTA> v_object_roles = oe.getRoles();
	

	while (it.hasNext()) {
	    float value = 0;
	    Map.Entry e = (Map.Entry)it.next();
	    AssociatedTrajectory at = (AssociatedTrajectory) e.getValue();
	    Trajectory t = (Trajectory) this.v_normal_trajectories.get(at.getId());
	    Vector<String> v_trajectory_roles = t.getRoles();

	    for(int i=0;i<v_object_roles.size();i++){
		MembershipTA moc = v_object_roles.elementAt(i);
		for(int j=0;j<v_trajectory_roles.size();j++){
		    String rol = v_trajectory_roles.elementAt(j);
		    if(rol.equals(moc.class_name) && moc.membership_value > value)
			value = moc.membership_value;
		}//end for
	    }
	    at.updateRoleSatisfaction(value);
	}//end while
	
	
    }//end method

    private void temporalAnalysis(ObjectEvent oe){
	
	ObjectLog obj = (ObjectLog) this.hm_objects.get(oe.getId());

	HashMap v_trajectories = obj.getAssociatedTrajectories();
	Iterator it = v_trajectories.entrySet().iterator();
	while (it.hasNext()) {
	    float satisfaction = 1;
	    Map.Entry e = (Map.Entry)it.next();
	    AssociatedTrajectory at = (AssociatedTrajectory) e.getValue();
	    Trajectory t = (Trajectory) this.v_normal_trajectories.get(at.getId());
	    long mt = t.getMaximumTime();

	    //analysing maximun time allowed to follow a particular trajectory.
	    if(mt > 0){
		int af = at.getFrame(); //frame in which the trajectory was associated to the object
		int cf = oe.getCurrentFrame(); //current frame
		//25 frames are a second. Taking into account this fact ...
		if(((long)cf-af) > mt){
		    //exceed time, temporal constraint no satisfied.
		    satisfaction = 0;
		}		
	    }//end if

	    //analysing temporal intervals
	    
	    if(satisfaction != 0){
		Vector<TimeInterval> v_intervals = (Vector) t.getIntervals();
		boolean next = true;		
		for(int i=0; i< v_intervals.size() && next; i++){
		    TimeInterval ti = v_intervals.elementAt(i);
		    TimeInstant t_start = ti.getStart();
		    TimeInstant t_end = ti.getEnd();
		    if(t_start.beforeNow() && t_end.afterNow()){
			satisfaction = 1;
			next = false;
		    }else{
			satisfaction = 0;
		    }
		}//for
	    }//end-if
	    //updating satisfaction degree
	    at.updateTemporalSatisfaction(satisfaction);//updating temporal satisfaction	
	}//end while	
    }//end temporalAnalysis

    private void spatialAnalysis(ObjectEvent oe){
	
	ObjectLog obj = (ObjectLog) this.hm_objects.get(oe.getId());
	if(obj == null){
	    obj = new ObjectLog(oe.getId());
	    this.hm_objects.put(oe.getId(), obj);
	}

	Vector<Zone> areas = oe.getZones();

	//first we check if is possible to associate new trajectories to the object obj. It is possible
	//only if some of the current zones in which the object could be located in, is a beginning area.	
	//and the trajectory is not associated to the object.

	//for every area in which the object could be located in
	for(int i=0;i<areas.size();i++){
	    Zone z = (Zone) areas.elementAt(i);
	    Iterator it = v_normal_trajectories.entrySet().iterator();
	    //for every normal trajectory defined in the environment.
	    while (it.hasNext()) {
		Map.Entry e = (Map.Entry)it.next();
		Trajectory t = (Trajectory) e.getValue();
		if(isBeginningZone(z,t) && !obj.isAssociatedTrajectory(t)){
		    AssociatedTrajectory at = new AssociatedTrajectory(oe.getCurrentFrame(),t.getId(),z);
		    obj.associateTrajectory(t.getId(),at);
		}//end else
	    }//end while
	}//end for
	//----------- end new trajectory association -------------
	
	//Updating associated trajectories according to the current object's location.
	HashMap v_trajectories = obj.getAssociatedTrajectories();
	Iterator it = v_trajectories.entrySet().iterator();
	
	Vector<String> rubbish  = new Vector();

	while (it.hasNext()) {
	    
	    Map.Entry e = (Map.Entry)it.next();
	    AssociatedTrajectory at = (AssociatedTrajectory) e.getValue();
	    Trajectory t = (Trajectory) this.v_normal_trajectories.get(at.getId());
	    Zone last_zone = (Zone) at.getLastZone(); //last included zone in the associated trajectory.
	    Zone before_last_zone = (Zone) at.getBeforeLastZone();
	    String next_zone = t.nextZone(last_zone.name); //next zone in the general definition of the trajectory
	    boolean flag = false;

	    for(int i=0; i<areas.size(); i++){
		Zone cz = (Zone) areas.elementAt(i);
		String current_zone = cz.name;
		if(current_zone.equals(last_zone.name)){
		    flag = true;
		    if(cz.membership_value > last_zone.membership_value)
			last_zone.membership_value = cz.membership_value;
		}else if(before_last_zone != null && current_zone.equals(before_last_zone.name)){
		    //etapa de transición entre zonas, el sistema puede creer que el objeto vuelve atrás
		    flag = true;
		    if(cz.membership_value > before_last_zone.membership_value)
		    before_last_zone.membership_value = cz.membership_value;
		}else if(current_zone.equals(next_zone)){
		    flag = true;
		    at.addZone(cz);
		    
		}
	    }//end for

	    if(!flag){
		rubbish.add(at.getId());
	    }else{
		//we calculate the degree of satisfaction of the spatial constraints
		at.updateSpatialSatisfaction();
	    }
	}//end while	

	//cleaning trajectory preveiously asociated to the object and not really being followed by the object.
	for(int i=0;i<rubbish.size();i++){
	    obj.removeAssociatedTrajectory(rubbish.elementAt(i));	    
	}
    }//end spatialAnalysis
    
    private boolean isBeginningZone(Zone z, Trajectory t){
	if((z.name).equals(t.getBeginningArea()))
	    return true;
	else
	    return false;
    }//end isBeginningZone


    public String toString(){
	String out ="";
	//printing trajectories
	out += "TRAJECTORIES:\n";
	Iterator it = v_normal_trajectories.entrySet().iterator();
	while (it.hasNext()) {
	    Map.Entry e = (Map.Entry)it.next();
	    Trajectory t = (Trajectory) e.getValue();
	    out += t.toString()+"\n";
	}
	return out;
    }//end to String

}//end class
